import type {
  ElementSaveType,
  IElementCSSOption,
  ProcessedElementConfig,
  ProcessedElementTagConfig,
} from '../../../../types/constants'
import type { ComponentStyleIdType } from '../../../../types/interfaces'
import { SearchBox } from '../../../../components/search'
import { customElement } from '../../../../element/config/custom_element'
import { ElementTag } from '../../../../element/config/element_tag'
import { WebElement } from '../../../../element/config/web_element'
import { checkElementHideSettings } from '../../../../element/service'
import { ElementCSSHandler } from '../../../../element/service/element_css_handler'
import {
  ElementHideCssOperation,
  ElementHideOperationType,
} from '../../../../element/service/types'
import { BaseModule } from '../../../../lib/abstract'
import { DynamicHide } from '../../../../lib/dynamic_hide'
import { queryJQuery } from '../../../../lib/element_query'
import { CopyButtonUpdateType } from '../../../../lib/utils/base/dom'
import { ToolKit } from '../../../../lib/utils/toolkit'
import {
  AppStyleCollections,
  BodyClasses,
  ComponentStyleId,
  DEFAULT_ELEMENT_CONFIG_VALUES,
  ELEMENT_SAVE_SEPARATOR,
  ElementConfigSource,
  ElementSelectorContentSourceType,
  ElementSelectorDOMScopeType,
  ElementSelectorPosition,
  EVENT_EMITTER_NAMES,
  logger,
  VisibilitySettingsManager,
} from '../../../../types/constants'
import { ConfigManager } from '../../../config_manager'
import { ModuleManager } from '../../../module_manager'
import { StyleManager } from '../../../style_manager'
import { styles } from './styles'

interface Config {
  enabled?: boolean
}

/**
 * 页面净化
 */
export class AdEngine extends BaseModule {
  readonly id: string = 'ad_engine'
  readonly styleId: string = ComponentStyleId[this.name]
  readonly isInitialized: boolean = false
  private readonly configManager: ConfigManager = ConfigManager.getInstance()
  private readonly elementTag: ElementTag = ElementTag.getInstance()
  private readonly webElement: WebElement = WebElement.getInstance()
  private readonly dynamicHide: DynamicHide = DynamicHide.getInstance()
  readonly supportedPlatforms: string[] = this.configManager.moduleSupportedPlatforms(this.name)
  private static instance: AdEngine
  private readonly CSSHandler: ElementCSSHandler = ElementCSSHandler.getInstance()
  readonly elementId: string
  private panel?: JQuery<HTMLElement>
  // 搜索相关
  private searchBox: SearchBox | undefined = undefined
  private searchQuery: string = ''

  config: Config = {
    enabled: true,
  }

  get name(): ComponentStyleIdType {
    return 'AdEngine'
  }

  constructor() {
    super()
    const { name_en } = customElement.getSelector('ad', '广告控制栏') || {}
    this.elementId = name_en || 'ad-control'
  }

  public static getInstance(): AdEngine {
    if (!AdEngine.instance) {
      AdEngine.instance = new AdEngine()
    }
    return AdEngine.instance
  }

  destroy(): Promise<void> | void {}

  core(): void {
    this.loadConfig()

    if (!this.config.enabled)
      return

    // 检查是否已存在面板
    const $ad = $(`#${this.elementId}`)
    if ($ad.length)
      return

    // 创建广告控制面板
    const panel = this.createControlPanel()
    $('body').append(panel)
    // 设置事件监听器
    this.setupEventListeners()
    // 默认折叠面板
    this.collapsePanel()
    logger.important(`[ADM] 🛡️ 控制面板已生成`)
  }

  async onToggleCallback(_id: string, checked: boolean): Promise<void> {
    ToolKit.dom.simpleToggleElement(checked, '#ad-control')
    checked && await ModuleManager.getInstance().initModule(this.name, () => $('#ad-control').length > 0)
  }

  /**
   * 检查元素是否应该显示
   * @param config 元素配置
   * @returns 是否应该显示
   */
  private checkElementShowState(config: ProcessedElementConfig): boolean {
    let state = ToolKit.ui.element.validateSelectorType(config) && config.show === true && config.enable === true
    if (state && config.notExistsHide && !$(config.selector).length) {
      state = false
    }
    return state
  }

  /**
   * 更新广告计数器
   */
  updateAdCounter(): void {
    this.updateAdVisibility()

    const filteredCountElem = $('#ad-control .filtered-count')
    const totalCountElem = $('#ad-control .total-count')

    const merged = this.webElement.getSelectors().concat(customElement.getAllSelectors())
    const validElements = merged
      .filter(obj => Object.keys(obj).length > 0) // 仅保留非空对象

    const total = validElements.filter(c => this.checkElementShowState(c)).length
    const filtered = $('#ad-control .filters input[type="checkbox"]:checked').length

    filteredCountElem.text(filtered)
    totalCountElem.text(total)
  }

  /**
   * 更新广告可见性
   */
  updateAdVisibility(): void {
    $('#ad-control .filters input[type="checkbox"]').each((_index, element) => {
      const $checkbox = $(element)

      const domScope: ElementSelectorDOMScopeType = $checkbox.data('dom-scope') || ElementSelectorDOMScopeType.Document
      const hostSelector: string = $checkbox.data('host-selector') || ''
      const selector: string = $checkbox.data('selector')

      const $item = queryJQuery(selector, { domScope, hostSelector })
      const hidden = this.CSSHandler.shouldElementHidden({
        domScope,
        hostSelector,
        selector,
      })

      const isHidden = ToolKit.ui.element.areAllHidden($item) || hidden
      $checkbox.prop('checked', isHidden)
    })
  }

  /**
   * 折叠面板
   */
  private collapsePanel(): void {
    if (!this.panel)
      return

    this.panel.toggleClass('collapsed')
    const isCollapsed = this.panel.hasClass('collapsed')

    if (!isCollapsed)
      this.eventEmitter.emit(EVENT_EMITTER_NAMES.refreshAdEngine)

    $('#ad-control').css('display', isCollapsed ? 'flex' : 'block')
    $('#ad-control .filters').toggle(!isCollapsed)
    $('#ad-control h3').toggle(!isCollapsed)
    $('#ad-control .toggle-panel')
      .css({
        'padding-right': isCollapsed ? '0' : '10px',
        'position': isCollapsed ? 'unset' : 'absolute',
      })
      .text(isCollapsed ? '⬆️' : '⬇️')
    $('#ad-control .search-container').toggle(!isCollapsed)
    $('#ad-control .ad-btn').css('display', isCollapsed ? 'none' : 'flex')
    $('#ad-control .content').css({
      'margin-bottom': isCollapsed ? '0' : '10px',
      'padding': isCollapsed ? '0' : '10px',
      'justify-content': isCollapsed ? 'center' : 'flex-start',
    })
  }

  private getStyles(): string {
    return styles(this.elementId)
  }

  /**
   * 获取位置顺序
   * @param position 位置
   * @returns 顺序
   */
  private getPositionOrder(position?: ElementSelectorPosition | undefined): number {
    const orderMap: Record<ElementSelectorPosition, number> = {
      [ElementSelectorPosition.TopLeft]: 1,
      [ElementSelectorPosition.TopCenter]: 2,
      [ElementSelectorPosition.TopRight]: 3,
      [ElementSelectorPosition.MiddleLeft]: 4,
      [ElementSelectorPosition.MiddleCenter]: 5,
      [ElementSelectorPosition.MiddleRight]: 6,
      [ElementSelectorPosition.BottomLeft]: 7,
      [ElementSelectorPosition.BottomCenter]: 8,
      [ElementSelectorPosition.BottomRight]: 9,
      [ElementSelectorPosition.Undefined]: 10,
    }
    return position ? orderMap[position] : 10
  }

  /**
   * 创建位置指示器
   * @param position 位置
   * @returns 指示器
   */
  private createPositionIndicator(position: string): string {
    const positionMap: Record<string, string> = {
      'top-left': '⬉',
      'top-center': '⬆',
      'top-right': '⬈',
      'middle-left': '⬅',
      'middle-center': '⬤',
      'middle-right': '➡',
      'bottom-left': '⬋',
      'bottom-center': '⬇',
      'bottom-right': '⬊',
    }
    const symbol = positionMap[position] || '🏷️'

    return `
      <span class="position-indicator" data-position="${position}">
        <span class="position-symbol">${symbol}</span>
      </span>
    `
  }

  /**
   * 添加过滤项
   * @param type 类型
   * @param element 元素配置
   */
  private addFilter(type: ElementSaveType, element: ProcessedElementConfig | ProcessedElementTagConfig): void {
    if (!this.panel) {
      logger.warn('[ADM] ⚠️ 面板不存在，跳过添加规则')
      return
    }

    const domScope = element.domScope || DEFAULT_ELEMENT_CONFIG_VALUES.domScope
    const hostSelector = element.hostSelector || DEFAULT_ELEMENT_CONFIG_VALUES.hostSelector
    const selector = 'selector' in element ? element.selector : element.tag
    const name_zh = element.nameZh
    const name_en = element.nameEn

    // 处理选择器双引号
    const { data: escapedSelector, escaped } = ToolKit.ui.element.escapeSelectorQuotes(selector)

    if (escaped)
      logger.warn(`[${this.name}] 检测到元素选择器'${name_zh}'包含双引号，已转义为单引号（请更新配置）`, selector)

    const hide = element.hide ?? DEFAULT_ELEMENT_CONFIG_VALUES.hide
    const isOfficial = 'contentSource' in element ? element.contentSource === ElementSelectorContentSourceType.Official : DEFAULT_ELEMENT_CONFIG_VALUES.contentSource

    // 加载用户设置
    const userSettings = VisibilitySettingsManager.getAll()
    const settingKey = `${type}${ELEMENT_SAVE_SEPARATOR}${name_en}${ELEMENT_SAVE_SEPARATOR}${escapedSelector}`
    // 如果没有设置，则使用默认值
    let isEnabled = userSettings[settingKey] !== undefined ? userSettings[settingKey] : hide

    const $el = ToolKit.ui.element.getElement(element)
    if ($el) {
      if (isEnabled && ToolKit.ui.element.areAllVisible($el)) {
        isEnabled = false
      }
      else if (!isEnabled && ToolKit.ui.element.areAllHidden($el)) {
        isEnabled = true
      }
    }

    const typeSymbol = isOfficial ? '' : ' 🟠'
    this.panel.find('.filters').append(`
      <label>
        <input type="checkbox" 
          ${isEnabled ? 'checked' : ''}
          data-type="${type}"
          data-name="${name_en}"
          ${domScope === ElementSelectorDOMScopeType.Document ? '' : `data-dom-scope="${domScope}"`}
          ${hostSelector ? `data-host-selector="${hostSelector}"` : ''}
          data-selector="${escapedSelector}">
        ${this.createPositionIndicator(element.position || 'undefined')}
        <span class="filter-name" style="font-size:13px;" title="该元素的选择器：${escapedSelector}">${name_zh}${typeSymbol}</span>
        <button class="copy-selector-btn" title="复制选择器"></button>
      </label>
    `)
  }

  /**
   * 设置事件监听器
   */
  private setupEventListeners(): void {
    if (!this.panel) {
      logger.warn('[ADM] ⚠️ 面板不存在，跳过设置事件监听器')
      return
    }

    // 绑定搜索框事件
    this.searchBox?.mount(this.panel)

    // 面板切换
    $('#ad-control .toggle-panel, #ad-control .ad-control-title').on('click', () => this.collapsePanel())

    // 复选框变化
    this.panel.on('change', 'input[type="checkbox"]', async (e) => {
      const $checkbox = $(e.currentTarget)
      const filterKey = this.getFilterKey($checkbox)
      const isEnabled = $checkbox.prop('checked')

      const userSettings = VisibilitySettingsManager.getAll()

      // 更新用户设置
      userSettings[filterKey] = isEnabled
      VisibilitySettingsManager.save(userSettings)

      const options = this.CSSHandler.processElementVisibilitySetting({
        key: filterKey,
        hide: isEnabled,
        source: ElementConfigSource.System,
      })

      // 应用设置
      const hideResult = checkElementHideSettings(options)
      const { isValid, operationType, requiresCssInjection, cssOperation, injectorType } = hideResult

      if (isValid) {
        switch (operationType) {
          case ElementHideOperationType.Static: {
            if (requiresCssInjection) {
              if (cssOperation === ElementHideCssOperation.Inject) {
                await this.CSSHandler.applyRule({
                  ...options,
                  injectorType,
                  operation: ElementHideCssOperation.Inject,
                })
              }
              else if (cssOperation === ElementHideCssOperation.Remove) {
                await this.CSSHandler.removeRule({
                  ...options,
                  injectorType,
                  operation: ElementHideCssOperation.Remove,
                })
              }
            }
            ToolKit.ui.element.dealElementVisible(options, true)
            break
          }
          case ElementHideOperationType.Dynamic: {
            this.dynamicHide.pollDynamicHideElements([options])
            break
          }
          case ElementHideOperationType.DynamicCancellation: {
            this.dynamicHide.cancelSelectorProcessing(options.selector)
            ToolKit.ui.element.dealElementVisible(options, true)
            break
          }
          case ElementHideOperationType.EventDriven: {
            options.handler!()
            break
          }
        }
      }
      else {
        logger.warn(`[${this.name}] ${hideResult.reason}：${options.selector}`)
      }

      this.eventEmitter.emit(EVENT_EMITTER_NAMES.refreshAdEngine)
    })

    // 按钮事件
    // 复制选择器按钮点击事件
    this.panel.on('click', '.copy-selector-btn', async (e) => {
      const $button = $(e.currentTarget)

      const selector: string = $button.siblings('input').data('selector') || ''

      // 复制内容
      await ToolKit.dom.performCopy(selector, $button, CopyButtonUpdateType.TextContent, { showPlaceholder: false })
    })

    // 刷新事件
    this.eventEmitter.on(EVENT_EMITTER_NAMES.refreshAdEngine, () => {
      this.updateAdCounter()
    })

    // 重置默认设置
    this.panel.find('.reset-default').on('click', () => {
      VisibilitySettingsManager.clear()
      ToolKit.ui.page.refreshPage()
    })

    // 全选/取消全选
    this.panel.find('.toggle-all').on('click', () => {
      const rules = this.getRules(true)
      this.saveRulesToStorage(true)
      this.updateCheckboxState(true)
      this.CSSHandler.addRules(rules)
    })
    this.panel.find('.cancel-toggle-all').on('click', () => {
      const rules = this.getRules(false)
      this.saveRulesToStorage(false)
      this.updateCheckboxState(false)
      this.CSSHandler.deleteRules(rules)
    })

    // 全局事件
    $(document)
      .on('click', (e) => { // 点击外部区域关闭（事件委托）
        if (this.panel && !this.panel.hasClass('collapsed')
          && !$(e.target).closest('#ad-control').length
          && !$(e.target).is('.toggle-panel')) {
          this.collapsePanel()
        }
      })
      .on('keydown', (e) => { // ESC键关闭（事件委托）
        if (e.key === 'Escape' && this.panel && !this.panel.hasClass('collapsed')) {
          this.collapsePanel()
        }
      })

    // 关闭按钮
    $('#ad-control .close-btn').on('click', () => {
      this.panel?.hide()
    })
  }

  /**
   * 获取全选/取消全选状态下的所有规则
   */
  getRules(hide: boolean): IElementCSSOption[] {
    let logRecord = false
    const rules: IElementCSSOption[] = []

    this.panel!.find('input[type="checkbox"]').each((_, element) => {
      const $checkbox = $(element)
      const filterKey = this.getFilterKey($checkbox)

      const options = this.CSSHandler.processElementVisibilitySetting({
        key: filterKey,
        hide,
        source: ElementConfigSource.System,
      })

      // 更新元素可见性
      ToolKit.ui.element.dealElementVisible(options, true)
      if (!logRecord) {
        logger.important(`[${this.name}] 更新元素可见性`)
        logRecord = true
      }

      rules.push({
        ...options,
        operation: hide ? ElementHideCssOperation.Inject : ElementHideCssOperation.Remove,
      })
    })

    return rules
  }

  /**
   * 保存所有规则到本地存储
   */
  saveRulesToStorage(hide: boolean): void {
    const userSettings = VisibilitySettingsManager.getAll()

    this.panel!.find('input[type="checkbox"]').each((_, element) => {
      const $checkbox = $(element)
      const filterKey = this.getFilterKey($checkbox)

      // 更新用户设置
      userSettings[filterKey] = hide
    })

    VisibilitySettingsManager.save(userSettings)
  }

  /**
   * 更新复选框状态
   */
  updateCheckboxState(hide: boolean): void {
    this.panel!.find('input[type="checkbox"]').prop('checked', hide)
  }

  /**
   * 移除无效的可见性设置
   * @param userSettings 可见性设置
   */
  removeInvalidSelectors(userSettings: Record<string, boolean>): void {
    const filterKeys = new Set<string>()
    this.panel?.find('input[type="checkbox"]').each((_, cb) => {
      const filterKey = this.getFilterKey($(cb))
      filterKeys.add(filterKey)
    })

    // 过滤出无效的 filterKey
    const invalidFilterKeys = Object.keys(userSettings).filter(key => !filterKeys.has(key))

    if (invalidFilterKeys.length > 0) {
      // 删除无效的设置
      VisibilitySettingsManager.delete(invalidFilterKeys)
      // 恢复可见
      this.restoreElementVisibility(invalidFilterKeys)
      logger.attention(`[${this.name}] 移除过时的元素可见性配置并恢复可见`, invalidFilterKeys)
    }
  }

  /**
   * 恢复元素可见性
   */
  restoreElementVisibility(keys: string[]): void {
    keys.forEach((key) => {
      const options = this.CSSHandler.processElementVisibilitySetting({
        key,
        hide: false,
        source: ElementConfigSource.System,
      })

      if (!options.enable)
        return

      ToolKit.ui.element.dealElementVisible(options, false)
    })
  }

  /**
   * 获取元素可见性设置的键值
   */
  getFilterKey($element: JQuery<HTMLElement>): string {
    const type = $element.data('type')
    const name = $element.data('name')
    const selector = $element.data('selector')

    return `${type}${ELEMENT_SAVE_SEPARATOR}${name}${ELEMENT_SAVE_SEPARATOR}${selector}`
  }

  /**
   * 创建控制面板
   * @returns 面板
   */
  private createControlPanel(): JQuery<HTMLElement> {
    if (this.panel)
      return this.panel

    // 注册样式
    StyleManager.getInstance().registerStyle(this.styleId, this.getStyles(), false, true)

    // 初始化搜索组件
    this.searchBox = new SearchBox({
      id: 'ad-control-search-box',
      placeholder: '搜索页面元素',
      onSearch: (value, _QuickSearch: boolean) => {
        this.searchQuery = value.toLowerCase().trim()
        this.handleSearchEvent()
      },
    })

    this.panel = $(`
      <div id="${this.elementId}">
        <div class="content">
          <h3 class="ad-control-title" title="【温馨提示】未选中规则对应的页面元素不一定未被隐藏，以实际页面元素是否隐藏为准">
            🛡️ 页面净化&nbsp;
            <span class="ad-counter">
              (<span class="filtered-count">0</span>/<span class="total-count">0</span>)
            </span>
          </h3>
          <button class="toggle-panel" class="collapsed">⬇️</button>
          <span class="close-btn" title="临时关闭当前面板">&#10005;</span>
        </div>
        <div class="search-container">
          ${this.searchBox.render()}
        </div>
        <div class="filters"></div>
        <div class="ad-btn">
          <button class="reset-default">恢复默认</button>
          <button class="cancel-toggle-all">取消全选</button>
          <button class="toggle-all">选择全部</button>
        </div>
      </div>
    `)

    // 标记为项目元素
    ToolKit.browser.platform.markProjectElement(this.panel)

    // 添加过滤项
    const elements = [
      ...(this.webElement.getSelectors() || []),
      ...(customElement.getAllSelectors() || []),
    ]
      .filter(el => this.checkElementShowState(el))
      .map(el => ({
        element: el,
        el: ToolKit.ui.element.getElement(el),
        positionOrder: this.getPositionOrder(el.position as ElementSelectorPosition),
        notExistsHide: el.notExistsHide,
      }))
      .filter(({ el, notExistsHide }) => el || (!el && !notExistsHide)) // 过滤掉无效元素
      .sort((a, b) => a.positionOrder - b.positionOrder)

    elements.forEach(({ element }) => {
      this.addFilter('element', element)
    })

    const elementTags = this.elementTag.getSelectors() || []
    elementTags
      .filter((tag: any) => tag.show && $(tag.tag).length)
      .forEach((tag: any) => {
        this.addFilter('tag', tag)
      })

    const $labels = $(this.panel).find('.filters label')
    $(this.panel).find('.filters').toggleClass(BodyClasses.IS_EMPTY, !$labels || $labels.length === 0)

    // 更新元素可见性设置
    this.updateElementVisibilitySettings()
    // 移除无效选择器
    this.removeInvalidSelectors(VisibilitySettingsManager.getAll())

    return this.panel
  }

  /**
   * 更新元素可见性设置
   */
  updateElementVisibilitySettings(): void {
    // 获取当前所有用户设置
    const userSettings = VisibilitySettingsManager.getAll()

    // 收集当前面板中所有的filterKey并建立基础键映射
    const currentKeyMap = new Map<string, string>()
    this.panel?.find('input[type="checkbox"]').each((_, cb) => {
      const filterKey = this.getFilterKey($(cb))
      const keyBase = this.getKeyBase(filterKey)

      if (!currentKeyMap.has(keyBase)) {
        currentKeyMap.set(keyBase, filterKey)
      }
    })

    // 找出不再存在的旧设置键
    const deprecatedKeys = Object.keys(userSettings).filter((key) => {
      const keyBase = this.getKeyBase(key)
      const matchingNewKey = currentKeyMap.get(keyBase) || ''
      return matchingNewKey && matchingNewKey !== key
    })

    // 迁移统计
    let updateCount = 0
    const updateDetails: Array<{
      oldKey: string
      newKey: string
      hideStatus: boolean
    }> = []

    // 迁移旧设置到新键名
    deprecatedKeys.forEach((oldKey) => {
      const shouldHide = userSettings[oldKey]

      // 直接从映射中获取对应的新键
      const matchingNewKey = currentKeyMap.get(this.getKeyBase(oldKey)) || ''

      if (matchingNewKey) {
        // 删除旧键
        delete userSettings[oldKey]
        // 新增新键
        userSettings[matchingNewKey] = shouldHide
        // 统计详情
        updateCount++
        updateDetails.push({
          oldKey,
          newKey: matchingNewKey,
          hideStatus: shouldHide,
        })
      }
    })

    if (updateCount === 0)
      return

    logger.attention(`[${this.name}] 更新 ${updateCount} 个可见性设置项`, updateDetails)
    // 更新用户设置
    VisibilitySettingsManager.save(userSettings)
    this.eventEmitter.emit(EVENT_EMITTER_NAMES.hideElement)
  }

  /**
   * 获取键的基础部分（前两部分）
   */
  private getKeyBase(key: string): string {
    const parts = key.split(ELEMENT_SAVE_SEPARATOR)
    return parts.slice(0, 2).join(ELEMENT_SAVE_SEPARATOR)
  }

  /**
   * 处理搜索事件
   */
  handleSearchEvent(): void {
    const $labels = this.panel?.find('.filters label')

    if (!$labels || $labels.length === 0)
      return

    if (!this.searchQuery) {
      $labels.removeClass(AppStyleCollections.Hide.ELEMENT)
      return
    }

    $labels.each((_, label) => {
      const $label = $(label)
      const text = $label.text().trim().toLowerCase()

      $label.toggleClass(AppStyleCollections.Hide.ELEMENT, !text.includes(this.searchQuery))
    })
  }

  applyConfig(_config: Partial<Config>): void {
  }
}
